<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="shortcut icon" type="image/png" href="../assets/icon.png">

<title reclang="PZM5">RecordApp测试</title>

<script>
var PageLM="2024-09-09 21:58";
(function(){
console.log("【温馨提示】本页面用于测试RecordApp的主要功能，代码难看臃肿，不建议阅读本页面源码；如果想要快速入门，请参考app-support-sample/QuickStart.html页面。    "
+"[Reminder] This page is used to test the main functions of RecordApp. The code is ugly and bloated. It is not recommended to read the source code of this page; if you want to get started quickly, please refer to the app-support-sample/QuickStart.html page.");

//加载核心库，其他类型支持库在下面根据用户点击选择加载，刷新页面可以恢复选择的类型
var srcC="src/recorder-core.js",distC="dist/recorder-core.js";
var initJss=[srcC];
var tei=window.TypeEngineImports={//不同类型编码器需要加载的js文件
	mp3:{src:[srcC,"src/engine/mp3.js","src/engine/mp3-engine.js",0,"src/engine/wav.js"]
		,dist:["recorder.mp3.min.js",0,"src/engine/wav.js"]}
	,wav:{src:[srcC,"src/engine/wav.js"]
		,dist:["recorder.wav.min.js"]}
	,pcm:{src:[srcC,"src/engine/pcm.js",0,"src/engine/wav.js"]
		,dist:[distC,"dist/engine/pcm.js",0,"src/engine/wav.js"]}
	,amr:{src:[srcC,"src/engine/beta-amr.js","src/engine/beta-amr-engine.js",0,"src/engine/wav.js"]
		,dist:[distC,"dist/engine/beta-amr.js",0,"src/engine/wav.js"]}
	,ogg:{src:[srcC,"src/engine/beta-ogg.js","src/engine/beta-ogg-engine.js"]
		,dist:[distC,"dist/engine/beta-ogg.js"]}
	,g711a:{},g711u:{}
	,webm:{name:"webm(beta)",sort:99,
		src:[srcC,"src/engine/beta-webm.js"]
		,dist:[distC,"dist/engine/beta-webm.js"]}
};
tei.g711a.src=tei.g711u.src=[srcC,"src/engine/g711x.js",0,"src/engine/wav.js"];
tei.g711a.dist=tei.g711u.dist=[distC,"dist/engine/g711x.js",0,"src/engine/wav.js"];

window.Import_RecJs_Set={};//刷新页面，加载选中的类型js
if(/recImport=([^?#&]+)/.test(location.href)){
	try{
		var o=JSON.parse(decodeURIComponent(RegExp.$1));
		var te=tei[o.type]||{},jss=te[o.minJs?"dist":"src"];
		if(!jss)throw "unknown type="+o.type;
		Import_RecJs_Set=o; initJss=jss;
		console.log("recImport init", o, jss);
	}catch(e){
		console.error("recImport init error",e);
	}
}
for(var i=0;i<initJss.length;i++){
	if(initJss[i]){
		document.write('<scr'+'ipt src="../'+initJss[i]+'"></scr'+'ipt>');
	}
}
})()</script>
</head>

<body>
<div class="main">

<!--加载RecordApp-->
<script src="../src/app-support/app.js"></script>
<!--加载Native支持文件，用于在Android、iOS App中调用原生录音-->
<script src="../src/app-support/app-native-support.js"></script>
<!--加载Native测试配置文件（实现Hybrid App的JsBridge调用）-->
<script src="./native-config.js"></script>

<!--加载可选扩展库-->
<script src="../src/extensions/waveview.js"></script>
<script src="../src/extensions/wavesurfer.view.js"></script>
<script src="../src/extensions/lib.fft.js"></script>
<script src="../src/extensions/frequency.histogram.view.js"></script>
<script src="../src/extensions/create-audio.nmn2pcm.js"></script>
<script src="../src/extensions/dtmf.encode.js"></script>
<script src="../src/extensions/dtmf.decode.js"></script>

<!--加载PlayBuffer-->
<script src="../assets/runtime-codes/fragment.playbuffer.js"></script>


<style>
body{
	word-wrap: break-word;
	word-break: break-all;
	background:#f5f5f5 center top no-repeat;
	background-size: auto 680px;
}
pre{
	white-space:pre-wrap;
}
label,label *{
	cursor: pointer;
}
label:hover{
	color:#06c;
}
a{
	text-decoration: none;
	color:#06c;
}
a:hover{
	color:#f00;
}

.main{
	max-width:700px;
	margin:0 auto;
	padding-bottom:80px
}

.mainBox{
	margin-top:12px;
	padding: 12px;
	border-radius: 6px;
	background: #fff;
	--border: 1px solid #f60;
	box-shadow: 2px 2px 3px #aaa;
}


.btns button,.mainBtn{
	display: inline-block;
	cursor: pointer;
	border: none;
	border-radius: 3px;
	background: #f60;
	color:#fff;
	padding: 0 15px;
	margin:3px 20px 3px 0;
	line-height: 36px;
	height: 36px;
	overflow: hidden;
	vertical-align: middle;
}
.btns button:active,.mainBtn:active{
	background: #f00;
}

.recwaveChoice{
	cursor: pointer;
	display:inline-block;
	vertical-align: bottom;
	border-right:1px solid #ccc;
	background:#ddd;
	line-height:28px;
	font-size:12px;
	color:#666;
	padding:0 5px;
}
.recwaveChoice:first-child{
	border-radius: 99px 0 0 99px;
}
.recwaveChoice:last-child{
	border-radius: 0 99px 99px 0;
	border-right:none;
}
.recwaveChoice.slc,.recwaveChoice:hover{
	background:#f60;
	color:#fff;
}

.lb{
	display:inline-block;
	vertical-align: middle;
	background:#00940e;
	color:#fff;
	font-size:14px;
	padding:2px 8px;
	border-radius: 99px;
}


.pd{
	padding:0 0 6px 0;
}
</style>

<script>
//兼容环境
function RandomKey(){
	return "randomkey"+(RandomKey.idx++);
};
RandomKey.idx=0;

//乐此不疲，古董浏览器，仅保证基本的可执行不代码异常
if(!Date.now)Date.now=function(){return new Date().getTime()};
if(!window.localStorage){window.localStorage={}};
</script>
<script src="../assets/ztest-jquery.min-1.9.1.js"></script>

<script src="../assets/ztest-page-i18n.js"></script>
<div class="i18nBox"></div>


<div class="mainBox">
	<style>
		.navItem{
			display:inline-block;
			width:45%;
			max-width:300px;
			vertical-align: top;
			background:#eee;
			border-bottom: 5px solid #ccc;
			box-shadow: 2px 2px 3px #ddd;
			color:#666;
			text-decoration:none;
			border-radius: 8px;
			padding: 0 5px 3px;
		}
		.navItem.slc{
			border-bottom: 5px solid #00940e;
			color:#f60;
		}
		.navItem:hover{
			color:#d44;
		}
		.navTitle{
			text-align: center;
			font-size:18px;
			font-weight: bold;
		}
		.navItem.slc .navDesc{
			color:#00940e;
		}
		.navDesc{
			font-size:12px;
		}
	</style>
	<a class="navItem" style="margin-right:2%;" href="../">
		<div class="navTitle">Recorder H5</div>
		<div class="navDesc" reclang="a2Uc">Recorder H5使用简单，功能丰富，支持PC、Android、iOS 14.3+</div>
	</a>
	
	<a class="navItem slc" href="./">
		<div class="navTitle">Recorder App</div>
		<div class="navDesc" reclang="ptyK">支持：浏览器WebView（H5）、各种使用js来构建的程序（App、小程序、UniApp、Electron、NodeJs）</div>
	</a>
	
	<div style="margin-top:8px;font-size:18px;font-weight:bold;color:#f60">
		<span reclang="v1fx">RecordApp：基于Recorder的跨平台录音解决方案</span>
	</div>
	<div style="padding-left:15px;padding-bottom:12px">
		<style>.c_ul{margin:0;list-style-type: square}.c_ul li{margin:5px 0;}</style>
		<div reclang="kYvU">- 支持H5页面录音（即本页面支持）：</div>
		<ul class="c_ul">
			<li reclang="Roj2">在浏览器中（含App WebView）默认使用Recorder H5进行录音</li>
			<li>
				<span reclang="c7mM">在开启了原生支持的App WebView中使用原生录音，App Demo:</span>
				<a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_android" target="_blank">Android</a>, <a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_ios" target="_blank">iOS</a>
			</li>
		</ul>
		<div>
			<span reclang="up8g">- 支持微信小程序录音，微信小程序Demo:</span>
			<a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/miniProgram-wx" target="_blank">miniProgram-wx</a>
		</div>
		<div>
			<span reclang="K3sX">- 支持uni-app录音：H5、Android iOS App、微信小程序，含组件示例:</span>
			<a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_UniApp" target="_blank">demo_UniApp</a>
		</div>
		<div reclang="sjDM">- 支持自行适配到各种js运行环境中录音，比如：Electron、NodeJs、各种小程序</div>
		<div reclang="5agh">- 以上环境中均可使用大部分的: 录音格式、实时处理、和可视化等扩展功能</div>
	</div>
	
	<div class="pd">
		<span class="lb" reclang="lIPR">源码仓库 :</span>
		<a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample" target="_blank">GitHub</a>
		| <a href="https://gitee.com/xiangyuecn/Recorder/tree/master/app-support-sample" target="_blank">Gitee</a>
		
		<span class="lb" reclang="ZNjk">更多Demo :</span> <a href="../assets/工具-代码运行和静态分发Runtime.html" target="_blank" reclang="Hkoo">Demo列表(可编辑)</a>
		| <a href="../assets/demo-vue/recordapp.html" target="_blank">App vue</a>
	</div>
	<div>
		<span class="lb">QuickStart :</span>
		<a href="QuickStart.html" target="_blank">/app-support-sample/QuickStart.html</a>
		<span style="font-size:12px;color:#999" reclang="paWp">(Copy即用，更适合入门学习)</span>
	</div>
</div>



<div class="mainBox">
	<div class="pd">
		<span class="lb" reclang="5lOo">类型 :</span> <span class="types"></span>
	</div>
	<div class="pd">
		<span class="lb" reclang="dsAF">提示 :</span> <span class="typeTips">-</span>
	</div>
	<div class="pd">
		<span class="lb" reclang="u4jV">比特率 :</span> <input type="text" class="bit" value="16" style="width:60px">
		<span reclang="06ha">kbps，越大音质越好</span>
	</div>
	<div class="pd">
		<span class="lb" reclang="4PRA">采样率 :</span> <input type="text" class="sample" value="16000" style="width:60px">
		<span reclang="e0ec">hz，越大细节越丰富</span>
	</div>
	<div>
		<span class="lb">AppUseH5 :</span> <label><input type="checkbox" class="alwaysAppUseH5"><span reclang="Wpwv">App里面总是使用Recorder H5录音</span></label>
	</div>
</div>

<div class="mainBox">
	<div>
		<span class="btns">
			<button onclick="recreq()" reclang="aoOU">请求权限</button>
			<button onclick="recstart()" reclang="3MeL">录制</button>
			<button onclick="recstop()" style="margin-right:80px;" reclang="9vcY">停止</button>
		</span>
		
		<span style="display: inline-block;">
			<button onclick="recstartAndAutoStop()" reclang="6CdW">录制+定时停止</button>
			<input value="0" class="autoStopTime" style="width:60px;text-align:right;">ms
		</span>
	</div>
	<div class="pd btns">
		<span style="display: inline-block;margin-right: 36px;">
			<button onclick="recpause()" reclang="hxxb">暂停</button>
			<button onclick="recresume()" reclang="Orkm">继续</button>
			<button onclick="recstopX()" reclang="Yna5">停止(仅清理)</button>
		</span>
		<span style="display: inline-block;">
			<button onclick="recPlayLast()" reclang="Wahr">播放</button>
			<button onclick="recUploadLast()" reclang="6Rqg">上传</button>
			<button onclick="recDownLast()" reclang="gXBH">本地下载</button>
		</span>
	</div>
	<div class="pd recpower">
		<div style="height:40px;width:300px;background:#999;position:relative;">
			<div class="recpowerx" style="height:40px;background:#0B1;position:absolute;"></div>
			<div class="recpowert" style="padding-left:50px; line-height:40px; position: relative;"></div>
		</div>
	</div>
	<div class="pd">
		<button onclick="recstop2()" class="batEnc" reclang="wFvj">批量编码</button>
		<input type="text" class="bits" value="8 to 96 step 8">
		<span reclang="ozCS">kbps 测试音质用的，除比特率外其他参数可调整</span>
	</div>
	<div class="pd">
		<div style="border:1px solid #ccc;display:inline-block"><div style="height:100px;width:300px;" class="recwave"></div></div>
		
		<span style="font-size:0">
			<span class="recwaveChoice" key="WaveView">WaveView</span>
			<span class="recwaveChoice" key="SurferView">SurferView</span>
			<span class="recwaveChoice" key="Histogram1">Histogram1</span>
			<span class="recwaveChoice" key="Histogram2">H...2</span>
			<span class="recwaveChoice" key="Histogram3">H...3</span>
		</span>
	</div>
	<div class="pd">
		<label><input type="checkbox" class="takeoffEncodeChunkSet"><span reclang="Hyuy">接管编码器输出（takeoffEncodeChunk）</span></label>
	</div>
	<div class="pd">
		<label><input type="checkbox" class="useAECSet"><span reclang="Kz8D">尝试启用回声消除（echoCancellation）</span></label>
	</div>
	<div class="pd">
		<label><input type="checkbox" class="realTimeSendSet"><span reclang="zb16">实时语音通话聊天对讲（WebSocket、WebRTC）</span></label>
		<div class="realTimeSendView" style="display:none;"></div>
	</div>
	<div class="">
		<label><input type="checkbox" class="asrSet"><span reclang="jCKC">实时语音识别、音频文件转文字，ASR</span></label>
		<div class="asrView" style="display:none;"></div>
	</div>
</div>


<div class="mainBox">
	<audio class="recPlay" controls style="display:none;width:100%"></audio>
	<div class="reclog"></div>
	<div class="recLastLog" style="position:fixed;z-index:2;width:20vw;min-width:200px;max-height:100px;overflow:auto;right:0;bottom:0;background:#fff;padding:5px 10px;border-radius:6px 0 0 0;box-shadow:-1px -1px 3px #ddd;font-size:13px"></div>
</div>


<div class="mainBox">
	<span class="lb" reclang="QFFW">测试App :</span>
	iOS Demo App: <a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_ios" reclang="WUY9">下载源码</a> <span reclang="AeNc">自行编译</span>
	
	，Android Demo App: <a href="https://gitee.com/xiangyuecn/Recorder/blob/master/app-support-sample/demo_android/app-debug.apk.zip" reclang="Uf5q">下载APK</a>
	(<span reclang="5zSj">40kb，删除.zip后缀，</span><a href="https://github.com/xiangyuecn/Recorder/tree/master/app-support-sample/demo_android" reclang="G0HC">源码</a>)
	
	<div>
		<span class="lb" reclang="zIlH">原生接口 :</span>
		<button onclick="callNative_showMemoryUsage()" reclang="dPz1">显示内存占用</button>
		<button onclick="callNative_notifyService(1)" reclang="dPz2">显示后台录音保活通知(Android)</button>
		<button onclick="callNative_notifyService(0)" reclang="dPz3">关闭通知</button>
		<button onclick="callNative_setSpeakerOff(false)" reclang="dPz4">切换成扬声器外放</button>
		<button onclick="callNative_setSpeakerOff(true)" reclang="dPz5">切换成听筒播放</button>
		<div style="color:#aaa;font-size:13px" reclang="dPz9">原生接口不一定可用，取决于原生app是否已实现对应接口</div>
	</div>
</div>

<div class="mainBox">
	<div style="color:#0ab;font-size:22px;font-weight:bold">
		<span reclang="Pea3">如需录音功能定制开发，网站、App、小程序、前端后端开发等需求，请加QQ群：①群 781036591、②群 748359095、③群 450721519，口令recorder，联系群主（即作者），谢谢~</span>
	</div>
</div>
	
<div class="mainBox">
	<span class="lb" reclang="2fWL">音乐播放测试 :</span>
	<button onclick="recplay2(this,'rec-4000ms-8kbps-16000hz.wav')">wav</button>
	<button onclick="recplay2(this,'rec-4000ms-64kbps-16000hz.mp3')">mp3</button>
	<button onclick="recplay2(this,'rec-4000ms-64kbps-16000hz.ogg')">ogg</button>
	<button onclick="recplay2(this,'rec-4000ms-64kbps-16000hz.webm')">webm</button>
	<button onclick="recplay2(this,'rec-4000ms-12.8kbps-8000hz.amr')">amr</button>
	<button onclick="recplay2(this,'nmn5')" reclang="uad6">合成5分钟wav</button>
	
	<span reclang="z9o0">Audio对录音的影响测试</span> (<a href="https://github.com/xiangyuecn/Recorder/issues/34">issues#34</a>) <span reclang="yStv">；低版本iOS Safari如果未开始过录音并且播放了音乐，然后后续录音可能会有问题；再现方法</span> (<a href="assets/ztest_apple_developer_forums_getusermedia.html">test apple developer forums</a>): <span reclang="4yox">刷新页面后首先先播放音乐，然后开始测试录音，会发现波形显示掉帧或者保持直线。另测试浏览器对音频的支持情况。</span>
</div>

<div class="mainBox">
	<span class="lb" reclang="fFQq">视频播放测试 :</span>
	<button onclick="videoTestPlay('')" reclang="6E53">播放mp4</button>
	<script>var videoTestPlay=function(attr){
		$('.videoTest').show().html('<video controls '+attr+' webkit-playsinline playsinline x5-video-player-type="h5" '
		+'style="width:370px;height:160px">'
		+'<source src="../assets/audio/movie-一代宗师-此一时彼一时.mp4.webm" type="video/mp4"/>' //fix safari
		+'</'+'video>').find('video')[0].play()
	}</script>
	
	<span reclang="ALiC">Video对录音的影响测试</span> (<a href="https://github.com/xiangyuecn/Recorder/issues/84">issues#84</a>) <span reclang="yshJ">；iOS Safari可能出现先播放视频，然后再开始录音，会自动播放视频的声音，但并未再现。</span>
	<button onclick="$('.videoTest').show()" reclang="CwnP">显示video</button>
	<button onclick="$('.videoTest').hide()" reclang="nFEO">隐藏video</button>
	<button onclick="$('.videoTest').html('')" reclang="WXIx">移除video</button>
	<button onclick="videoTestPlay(' loop')" reclang="iXZv">循环播放</button>
	<button onclick="videoTestPlay(' loop muted')" reclang="2RpC">静音循环播放</button>
	<div class="videoTest"></div>
</div>

<div class="mainBox">
	<span class="lb" reclang="IClu">浏览器环境情况 :</span>
	<pre class="recinfoCode"></pre><textarea class="recinfoCodeTxt" style="display:none">
Platforms:${RecordApp.Current.Key}
IsApp:${isApp}

AudioContext:${"AudioContext" in window}
webkitAudioContext:${"webkitAudioContext" in window}
mediaDevices:${!!navigator.mediaDevices}
mediaDevices.getUserMedia:${!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)}
navigator.getUserMedia:${!!navigator.getUserMedia}
navigator.webkitGetUserMedia:${!!navigator.webkitGetUserMedia}
AudioContext.scriptProcessor:{{
						"createScriptProcessor" in (Recorder.Ctx||{})
					 || "createJavaScriptNode" in (Recorder.Ctx||{})
					}}
AudioContext.audioWorklet:{{"audioWorklet" in (Recorder.Ctx||{})}}
AudioWorkletNode:${"AudioWorkletNode" in window}
MediaRecorder:${"MediaRecorder" in window}
MediaRecorder.ondataavailable:${"MediaRecorder" in window && "ondataavailable" in MediaRecorder.prototype}
MediaRecorder.WebM.PCM:${"MediaRecorder" in window && MediaRecorder.isTypeSupported("audio/webm; codecs=pcm")}

URL:${location.href.replace(/#.+/g,"")}
UA:${navigator.userAgent}

<span reclang="mt71">RecordApp库修改时间（有可能修改了忘改）：</span>${RecordApp.LM}
<span reclang="ySaK">Recorder库修改时间（有可能修改了忘改）：</span>${Recorder.LM}
<span reclang="Ls9o">本页面修改时间（有可能修改了忘改）：</span>${PageLM}
</textarea>
</div>



<script>
function reclog(s,color){
	var now=new Date();
	var t=("0"+now.getHours()).substr(-2)
		+":"+("0"+now.getMinutes()).substr(-2)
		+":"+("0"+now.getSeconds()).substr(-2);
	var html='<div style="color:'+(!color?"":color==1?"red":color==2?"#0b1":color)+'">['+t+']'+s+'</div>';
	$(".reclog").prepend(html);
	$(".recLastLog").html(html.replace(/class\s*=/ig,"clazz="));
};
window.onerror=function(message, url, lineNo, columnNo, error){
	//https://www.cnblogs.com/xianyulaodi/p/6201829.html
	reclog('【Uncaught Error】'+message+'<pre>'+"at:"+lineNo+":"+columnNo+" url:"+url+"\n"+(error&&error.stack||Html_$T("jwqb::不能获得错误堆栈"))+'</pre>',1);
};
</script>



<script>
function baseSet(){
	var alwaysAppUseH5=$(".alwaysAppUseH5")[0].checked;
	if(!!RecordApp.alwaysAppUseH5Prev!=alwaysAppUseH5){
		reclog(Html_$T("T6A6::AppUseH5选项变更，已重置RecordApp，请先进行权限测试"));
		RecordApp.Current=null;
		RecordApp.alwaysAppUseH5Prev=alwaysAppUseH5;
	};
	RecordApp.AlwaysAppUseH5=alwaysAppUseH5;
	
	cancelAutoStop();
};
var reqUseAEC;
function recreq(call){
	call||(call=function(msg){
		msg&&reclog(msg,1);
	});
	baseSet();
	reclog(Html_$T("LnQj::开始请求授权..."));
	
	reqUseAEC=$(".useAECSet")[0].checked;
	if(reqUseAEC){
		//这个是Start中的audioTrackSet配置，在h5中必须提前配置，因为h5中RequestPermission会直接打开录音
		RecordApp.RequestPermission_H5OpenSet={ audioTrackSet:{ echoCancellation:true,noiseSuppression:true,autoGainControl:true } };
		reclog(Html_$T("W5dU::已启用audioTrackSet配置：")+JSON.stringify(RecordApp.RequestPermission_H5OpenSet.audioTrackSet));
	};
	
	RecordApp.RequestPermission(function(){
		reclog(RecordApp.Current.Key+" "+Html_$T("wnd4::已授权"),2);
		call();
	},function(err,isUserNotAllow){
		call((RecordApp.Current&&RecordApp.Current.Key||"[?]")
			+(isUserNotAllow?" UserNotAllow, ":"")
			+" "+Html_$T("oFqf::授权失败：")+err);
	});
};


var curSet,autoStopTimer;
function recstartAndAutoStop(){
	var time=+$(".autoStopTime").val()||0;
	if(time<100){
		reclog(Html_$T("9ZjY::定时不能小于100ms"),1);
		return;
	};
	recstart(function(msg){
		if(msg){
			msg&&reclog(msg,1);
			return;
		};
		reclog(Html_$T("7Jp4::定时{1}ms后自动停止录音",0,time));
		autoStopTimer=setTimeout(function(){
			autoStopTimer=0;
			reclog(Html_$T("MHeo::定时时间到，开始自动调用停止..."));
			recstop();
		},time);
	});
};
var cancelAutoStop=function(){
	if(autoStopTimer){
		reclog(Html_$T("K57T::已取消定时停止"),1);
		clearTimeout(autoStopTimer);
		autoStopTimer=0;
	};
};
function recstart(call){
	call||(call=function(msg){
		msg&&reclog(msg,1);
	});
	baseSet();
	if(!RecordApp.Current){
		call(Html_$T("dejy::需先调用RequestPermission"));
		return;
	};
	if(RecordApp.Current==RecordApp.Platforms.Native){
		reclog(Html_$T("Knhl::正在使用Native录音，底层由App原生层提供支持"));
	}else{
		reclog(Html_$T("mHN7::正在使用H5录音，底层由Recorder直接提供支持"));
	};
	
	var type=$("[name=type]:checked").val();
	var bit=+$(".bit").val();
	var sample=+$(".sample").val();
	window.waveStore={};
	window.takeoffChunks=[];
	
	var realTimeSendSet=$(".realTimeSendSet")[0].checked;
	if(realTimeSendSet&&!RecordApp.Current.CanProcess()){
		reclog(Html_$T("GM5h::当前环境{1}不支持实时回调，不能模拟实时编码传输",0,RecordApp.Current.Key),1);
	};
	
	var asrSet=$(".asrSet")[0].checked;
	if(asrSet&&!RecordApp.Current.CanProcess()){
		reclog(Html_$T("Vee6::当前环境{1}不支持实时回调，不能进行实时语音识别",0,RecordApp.Current.Key),1);
	};
	
	var takeoffEncodeChunkSet=$(".takeoffEncodeChunkSet")[0].checked;
	
	
	var set={
		type:type
		,bitRate:bit
		,sampleRate:sample
		,audioTrackSet:!reqUseAEC?null:{echoCancellation:true,noiseSuppression:true,autoGainControl:true} //配置回声消除，注意：H5中需要在请求录音权限前进行相同配置RecordApp.RequestPermission_H5OpenSet后此配置才会生效
		,onProcess:function(buffers,powerLevel,duration,sampleRate,newBufferIdx){
			$(".recpowerx").css("width",powerLevel+"%");
			$(".recpowert").text(formatMs(duration,1)+" / "+powerLevel);
			processTime=Date.now();
			
			//可视化图形绘制
			if(waveStore[recwaveChoiceKey]){
				if(waveStore.choice!=recwaveChoiceKey){
					waveStore.choice=recwaveChoiceKey;
					$(".recwave").html("").append(waveStore[recwaveChoiceKey].elem);
				};
				waveStore[recwaveChoiceKey].input(buffers[buffers.length-1],powerLevel,sampleRate);
			};
			
			//实时传输
			if(realTimeSendSet&&window.realTimeSendTry){
				realTimeSendTry(set,buffers,sampleRate);
			};
			if(realTimeSendSet&&window.RtVoiceProcess){
				RtVoiceProcess(buffers,powerLevel,duration,sampleRate,newBufferIdx);
			};
			//实时语音识别
			if(asrSet&&window.asrInput){
				asrInput(buffers,sampleRate,newBufferIdx);
			};
		}
		,takeoffEncodeChunk:!takeoffEncodeChunkSet?null:function(chunkBytes){
			takeoffChunks.push(chunkBytes);
		}
	};
	var processTime=0;
	curSet=null;
	reclog(RecordApp.Current.Key+" "+Html_$T("wFnI::正在打开..."));
	RecordApp.Start(set,function(){
		curSet=set;
		reclog(RecordApp.Current.Key+" "+Html_$T("bYp4::录制中:")+type+" "+bit+"kbps",2);
		window.realTimeSendTryReset&&realTimeSendTryReset(set);
		
		//此处创建这些音频可视化图形绘制浏览器支持妥妥的
		initWaveStore(waveStore,".recwave");
		
		//【稳如老狗WDT】可选的，监控是否在正常录音有onProcess回调，如果长时间没有回调就代表录音不正常
		var this_=   RecordApp; //有this就用this，没有就用一个全局对象
		if(RecordApp.Current.CanProcess()){
			var wdt=this_.watchDogTimer=setInterval(function(){
				if(wdt!=this_.watchDogTimer){ clearInterval(wdt); return } //sync
				if(Date.now()<this_.wdtPauseT) return; //如果暂停录音了就不检测：puase时赋值this_.wdtPauseT=Date.now()*2（永不监控），resume时赋值this_.wdtPauseT=Date.now()+1000（1秒后再监控）
				if(Date.now()-(processTime||startTime)>1500){ clearInterval(wdt);
					reclog(processTime?Html_$T("eWu1::录音被中断"):Html_$T("eWu2::录音未能正常开始"),1);
					// ... 错误处理，关闭录音，提醒用户
				}
			},1000);
		}else{
			reclog(Html_$T("eWu3::当前环境不支持onProcess回调，不启用watchDogTimer"),"#aaa"); //目前都支持回调
		}
		var startTime=Date.now(); this_.wdtPauseT=0;
		
		call();
	},function(err){
		call(RecordApp.Current.Key+" "+Html_$T("EBjo::开始录音失败：")+err);
	});
};
function recpause(){
	if(RecordApp.GetCurrentRecOrNull()){
		RecordApp.Pause();
		var this_=RecordApp;this_.wdtPauseT=Date.now()*2; //永不监控onProcess超时
		reclog(Html_$T("z2B2::已暂停"));
	}
};
function recresume(){
	if(RecordApp.GetCurrentRecOrNull()){
		RecordApp.Resume();
		var this_=RecordApp;this_.wdtPauseT=Date.now()+1000; //1秒后再监控onProcess超时
		reclog(Html_$T("sTFX::继续录音中..."));
	}
};
function recstopX(){
	cancelAutoStop();
	var this_=RecordApp;this_.watchDogTimer=0; //停止监控onProcess超时
	RecordApp.Stop(
		null //success传null就只会清理资源，不会进行转码
		,function(msg){
			reclog(Html_$T("C7au::已清理，错误信息：")+msg);
		}
	);
};
var recblob={},stopRec;
function recstop(call){
	var set=curSet;
	recstopFn(call,true,function(err,blob,time){
		setTimeout(function(){
			window.realTimeSendTryStop&&realTimeSendTryStop(set);
			
			if(!err && set.takeoffEncodeChunk){
				reclog(Html_$T("aS2G::启用takeoffEncodeChunk后Stop返回的blob长度为0不提供音频数据"),"#f60");
				reclog(Html_$T("Vsr4::takeoffEncodeChunk接收到{1}片音频片段，正在合并成一个音频文件...",0,takeoffChunks.length));
				var len=0;
				for(var i=0;i<takeoffChunks.length;i++){
					len+=takeoffChunks[i].length;
				};
				var chunkData=new Uint8Array(len);
				for(var i=0,idx=0;i<takeoffChunks.length;i++){
					var itm=takeoffChunks[i];
					chunkData.set(itm,idx);
					idx+=itm.length;
				};
				var blob=new Blob([chunkData],{type:"audio/"+set.type});
				addRecLog(time,Html_$T("NcRr::合并"),blob,set,Date.now());
			};
		});
	});
};
function recstopFn(call,isClick,endCall,rec){
	cancelAutoStop();
	call||(call=function(msg){
		msg&&reclog(msg,1);
	});
	
	var t1=Date.now();
	if(!isClick){
		rec.stop(function(blob,time){
			var tag=endCall("",blob,time);
			if(tag==-1){
				return;
			};
			
			addRecLog(time,tag||Html_$T("SS8P::已录制"),blob,rec.set,t1);
		},function(s){
			reclog(Html_$T("Xnib::失败：")+s);
			endCall(s);
		});
		return;
	};
	
	var setData=curSet;
	curSet=null;
	
	if(!setData){
		//没有开始？不管，stop清理资源
		setData={};
	}else{
		reclog(RecordApp.Current.Key+" "+Html_$T("s99V::正在结束")+" "+setData.type+"...");
	};
	
	var this_=RecordApp;this_.watchDogTimer=0; //停止监控onProcess超时
	RecordApp.Stop(function(aBuf,time,mime){
		var blob=new Blob([aBuf],{type:mime});
		endCall("",blob,time);
		stopRec=RecordApp.GetCurrentRecOrNull();
		
		addRecLog(time,Html_$T("jpEQ::已录制"),blob,setData,t1);
		call(null,{data:blob,duration:time});
	},function(s){
		endCall(s);
		call(Html_$T("lenm::失败：")+s);
	});
};
var recLogLast;
var addRecLog=function(time,tag,blob,set,t1){
	var id=RandomKey(16);
	recLogLast={blob:blob,set:$.extend({},set),time:time,key:id};
	recblob[id]=recLogLast;
	var a1=intp(Date.now()-t1,4),a2=intp(blob.size,6);
	reclog(tag+":"+intp(set.bitRate,3)+"kbps "+intp(set.sampleRate,5)+"hz "
		+Html_$T("aZ4T::花{1}ms编码{2}B",0,a1,a2)
		+" ["+set.type+"]"+formatMs(time)+'ms'
		+' <button onclick="recdown(\''+id+'\')">'+Html_$T("9aiF::下载")+'</button>'
		+' <button onclick="recplay(\''+id+'\')">'+Html_$T("56g1::播放")+'</button>'
		+' <span class="p'+id+'"></span> <span class="d'+id+'"></span>');
};
var intp=function(s,len){
	s=s==null?"-":s+"";
	if(s.length>=len)return s;
	return ("_______"+s).substr(-len);
};
var formatMs=function(ms,all){
	var ss=ms%1000;ms=(ms-ss)/1000;
	var s=ms%60;ms=(ms-s)/60;
	var m=ms%60;ms=(ms-m)/60;
	var h=ms;
	var t=(h?h+":":"")
		+(all||h+m?("0"+m).substr(-2)+":":"")
		+(all||h+m+s?("0"+s).substr(-2)+"″":"")
		+("00"+ss).substr(-3);
	return t;
};
function recstop2(){
	reclog(Html_$T("LZkZ::使用RecordApp.GetCurrentRecOrNull()方法，我们可以在停止录音时，得到RecordApp编码用的rec对象，因此可以进行二次编码。"),2);
	
	var rec=stopRec;
	if(!rec||!rec.buffers){
		reclog(Html_$T("qKJ9::需先录个音"),1);
		return;
	};
	
	var type=$("[name=type]:checked").val();
	var sample=+$(".sample").val();
	var bits=/(\d+)\s+to\s+(\d+)\s+step\s+(\d+)\s*/i.exec($(".bits").val());
	if(!bits){
		reclog(Html_$T("J1wQ::码率列表有误，需要? to ? step ?结构"));
		return;
	};
	reclog(Html_$T("OQV2::开始批量编码，请勿进行其他操作~"));
	
	rec.set.type=type;
	rec.set.sampleRate=sample;
	
	var list=[];
	for(var i=+bits[1];i<+bits[2]+1;i+=+bits[3]){
		list.push(i);
	};
	if(rec.set.type=="wav"){
		list=[8,16];
	};
	if(rec.set.type=="amr"){
		list=[4.75, 5.15, 5.9, 6.7, 7.4, 7.95, 10.2, 12.2];
	};
	
	
	var i=-1;
	var bak=rec.set.bitRate;
	var run=function(){
		i++;
		if(i>=list.length){
			rec.set.bitRate=bak;
			reclog(Html_$T("nUq7::批量编码完成"));
			return;
		};
		rec.set.bitRate=list[i];
		rec.isMock=1;
		rec.dataType="blob";
		var t1=Date.now();
		rec.stop(function(blob,time){
			addRecLog(time,Html_$T("A7H4::已编码"),blob,rec.set,t1);
			run();
		},function(s){
			reclog(Html_$T("mFBf::失败：")+s,1);
		});
	};
	run();
};
function recplay(key){
	var audio=$(".recPlay")[0];
	audio.style.display="inline-block";
	if(!(audio.ended || audio.paused)){
		audio.pause();
	};
	
	var o=recblob[key];
	if(o){
		o.play=(o.play||0)+1;
		var logmsg=function(msg){
			$(".p"+key).html('<span style="color:green">'+o.play+'</span> '+new Date().toLocaleTimeString()+" "+msg);
		};
		logmsg("");
		audio.onerror=function(e){
			logmsg('<span style="color:red">'+Html_$T("kj0b::播放失败")+'['+audio.error.code+']'+audio.error.message+'</span>');
		};
		
		if(o.play2Name){
			o.play--;
			if(o.play2Msg){
				logmsg(o.play2Msg);
			}else if(o.play2Err){
				logmsg('<span style="color:red">'+o.play2Err+'</span>');
			}else{
				o.play++;
				audio.src=o.play2Url||("../assets/audio/"+o.play2Name);
				audio.play();
			}
			return;
		};
		var end=function(blob){
			audio.src=(window.URL||webkitURL).createObjectURL(blob);
			audio.play();
		};
		var wav=Recorder[o.set.type+"2wav"];
		if(wav){
			logmsg(Html_$T("Jysa::正在转码成wav..."));
			var wavData=o.blob;
			if(o.set.type=="pcm"){
				wavData={
					sampleRate:o.set.sampleRate
					,bitRate:o.set.bitRate
					,blob:o.blob
				};
			};
			wav(wavData,function(blob){
				end(blob);
				logmsg(Html_$T("zbUk::已转码成wav播放"));
			},function(msg){
				logmsg('<span style="color:red">'+Html_$T('v8S9::转码成wav失败：')+msg+'</span>');
			});
		}else{
			end(o.blob);
		};
	};
};
function recplay2(elem,name){
	var url="";
	var run=function(msg,err){
		elem=$(elem);
		var key="recplay2"+elem.html();
		Object.assign(recblob[key]||(recblob[key]={}),{
			play2Name:name
			,play2Url:url
			,play2Msg:msg
			,play2Err:err
		});
		if(!$(".p"+key).length){
			elem.before('<br>');
			elem.after('<span class="p'+key+'"></span><br>');
		};
		
		recplay(key);
	};
	
	if(name=="nmn5"){ //合成一个5分钟的wav
		if(window.nmn5Url){
			url=window.nmn5Url; return run();
		}
		var sr=16000;
		var pcm=Recorder.NMN2PCM.GetExamples().Canon.get(sr).pcm;
		var pcm5=new Int16Array(5*60*sr),n=0;
		while(n<pcm5.length){
			var s=Math.min(pcm.length, pcm5.length-n);
			pcm5.set(s==pcm.length?pcm:pcm.subarray(0,s),n); n+=pcm.length
		}
		run(Html_$T("q2iG::正在合成wav..."));
		var mock=Recorder({ type:"wav",sampleRate:sr,bitRate:16 });
		mock.mock(pcm5, sr);
		mock.stop(function(blob){
			url=window.nmn5Url=(window.URL||webkitURL).createObjectURL(blob);
			run();
		},function(err){
			run("",err);
		});
	}else{ //播放音频文件
		run();
	}
};
function recPlayLast(){
	if(!recLogLast){
		reclog(Html_$T("U3nx::请先录音，然后停止后再播放"),1);
		return;
	};
	recplay(recLogLast.key);
};
function recUploadLast(){
	if(!recLogLast){
		reclog(Html_$T("8y8L::请先录音，然后停止后再上传"),1);
		return;
	};
	var blob=recLogLast.blob;
	
	//本例子假设使用原始XMLHttpRequest请求方式，实际使用中自行调整为自己的请求方式
	//录音结束时拿到了blob文件对象，可以用FileReader读取出内容，或者用FormData上传
	var api="http://127.0.0.1:9528";
	var onreadystatechange=function(xhr,title){
		return function(){
			if(xhr.readyState==4){
				if(xhr.status==200){
					reclog(title+Html_$T("ebkz::上传成功")+' <span style="color:#999">response: '+xhr.responseText+'</span>',2);
				}else{
					reclog(title+Html_$T("p3cq::没有完成上传，演示上传地址无需关注上传结果，只要浏览器控制台内Network面板内看到的请求数据结构是预期的就ok了。"), "#d8c1a0");
					
					console.error(Html_xT(title+Html_$T("rscd::上传失败")),xhr.status,xhr.responseText);
				};
			};
		};
	};
	reclog(Html_$T("t0Vr::开始上传到{1}，请稍候... （你可以先到源码 /assets/node-localServer 目录内执行 npm run start 来运行本地测试服务器）",0,api));

	/***方式一：将blob文件转成base64纯文本编码，使用普通application/x-www-form-urlencoded表单上传***/
	var reader=new FileReader();
	reader.onloadend=function(){
		var postData="";
		postData+="mime="+encodeURIComponent(blob.type);//告诉后端，这个录音是什么格式的，可能前后端都固定的mp3可以不用写
		postData+="&upfile_b64="+encodeURIComponent((/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1]) //录音文件内容，后端进行base64解码成二进制
		//...其他表单参数
		
		var xhr=new XMLHttpRequest();
		xhr.open("POST", api+"/uploadBase64");
		xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		xhr.onreadystatechange=onreadystatechange(xhr,Html_$T("3Auy::上传方式一【Base64】"));
		xhr.send(postData);
	};
	reader.readAsDataURL(blob);

	/***方式二：使用FormData用multipart/form-data表单上传文件***/
	var form=new FormData();
	form.append("upfile",blob,"recorder.mp3"); //和普通form表单并无二致，后端接收到upfile参数的文件，文件名为recorder.mp3
	//...其他表单参数
	
	var xhr=new XMLHttpRequest();
	xhr.open("POST", api+"/upload");
	xhr.onreadystatechange=onreadystatechange(xhr,Html_$T("GEBC::上传方式二【FormData】"));
	xhr.send(form);
};
function recDownLast(){
	if(!recLogLast){
		reclog(Html_$T("g1CD::请先录音，然后停止后再下载"),1);
		return;
	};
	recdown(recLogLast.key);
};
function recdown(key){
	var o=recblob[key];
	if(o){
		var cls=RandomKey(16);
		var name="rec-"+o.time+"ms-"+o.set.bitRate+"kbps-"+o.set.sampleRate+"hz."+o.set.type;
		
		o.down=(o.down||0)+1;
		$(".d"+key).html('<span style="color:red">'+o.down+'</span> '
			+Html_$T('Lw0c::点击{1}',0,"")+' <span class="'+cls+'"> '
			+Html_$T('cs8P::下载，或复制文本')
			+'<button onclick="recdown64(\''+key+'\',\''+cls+'\')">'+Html_$T('hziK::生成Base64文本')+'</button></span>');
		
		var downA=document.createElement("A");
		downA.innerHTML=Html_$T("rxa0::下载 ")+name;
		downA.href=(window.URL||webkitURL).createObjectURL(o.blob);
		downA.download=name;
		$("."+cls).prepend(downA);
		if(/mobile/i.test(navigator.userAgent)){
			alert(Html_xT(Html_$T("V48u::因移动端绝大部分国产浏览器未适配Blob Url的下载，所以本demo代码在移动端未调用downA.click()。请尝试点击日志中显示的下载链接下载，无法下载就复制Base64")));
		}else{
			downA.click();
		}
	};
};
function recdown64(key, cls){
	var o=recblob[key];
	
	var reader = new FileReader();
	reader.onloadend = function() {
		var id=RandomKey(16);
		$("."+cls).append('<textarea class="'+id+'"></textarea>');
		$("."+id).val(reader.result);
	};
	reader.readAsDataURL(o.blob);
};


var s="https://github.com/xiangyuecn/Recorder/blob/master/src/extensions/";
var getExtensionsInfo=function(){ return {
	WaveView:'<b>WaveView</b> (<a href="'+s+'waveview.js">waveview.js</a> '+Html_$T("RDIF::动态波形")+')'
	,SurferView:'<b>WaveSurferView</b> (<a href="'+s+'wavesurfer.view.js">wavesurfer.view.js</a> '+Html_$T("08jd::音频可视化波形")+')'
	,Histogram:'<b>FrequencyHistogramView</b> (<a href="'+s+'frequency.histogram.view.js">frequency.histogram.view.js</a> + <a href="'+s+'lib.fft.js">lib.fft.js</a> '+Html_$T("JrUV::音频可视化频率直方图")+')'
}};
var recwaveChoiceKey=localStorage["RecWaveChoiceKey"]||"WaveView";
$(".recwaveChoice").bind("click",function(e){
	var elem=$(e.target);
	$(".recwaveChoice").removeClass("slc");
	var val=elem.addClass("slc").attr("key");
	var info=getExtensionsInfo()[val.replace(/\d+$/,"")];
	if(recwaveChoiceKey!=val){
		reclog(Html_$T("GGPu::已切换波形显示为：")+info);
	};
	recwaveChoiceKey=val;
	localStorage["RecWaveChoiceKey"]=recwaveChoiceKey;
});
if(!$(".recwaveChoice[key="+recwaveChoiceKey+"]").length){
	recwaveChoiceKey="WaveView";
};
$(".recwaveChoice[key="+recwaveChoiceKey+"]").click();

var initWaveStore=function(store,elem){
	store.WaveView=Recorder.WaveView({elem:elem});
	store.SurferView=Recorder.WaveSurferView({elem:elem});
	store.Histogram1=Recorder.FrequencyHistogramView({elem:elem});
	store.Histogram2=Recorder.FrequencyHistogramView({
		elem:elem
		,lineCount:200,widthRatio:1
		,position:0
		,minHeight:1
		,fallDuration:600
		,stripeEnable:false
		,mirrorEnable:true
	});
	store.Histogram3=Recorder.FrequencyHistogramView({
		elem:elem
		,lineCount:20
		,position:0
		,minHeight:1
		,fallDuration:400
		,stripeEnable:false
		,mirrorEnable:true
		,linear:[0,"#0ac",1,"#0ac"]
	});
};


reclog(Html_$T("x6fO::点击录制开始哦"));
var dh=Html_$T('U67F::、'),extensionsInfo=getExtensionsInfo();
reclog(Html_$T('4DQX::已启用Extensions：')
	+extensionsInfo.WaveView
	+dh+extensionsInfo.SurferView
	+dh+extensionsInfo.Histogram);

if(window.top!=window){
	var isSelf=false;
	try{
		window.top.aa=123;
		isSelf=true;
	}catch(e){};
	
	reclog("<span style='color:#f60'>"+Html_$T("m96f::当前页面处在在iframe中，但故意未进行任何处理，")+(isSelf?Html_$T("9ywI::当前是同域"):Html_$T("fLAB::并且已发生跨域，未设置相应策略H5录音权限永远是拒绝的，Native使用了postMessage转发兼容方案"))+"</span>");
};




//实时传输数据模拟开关
$(".realTimeSendSet").bind("change",function(e){
	var open=e.target.checked;
	$(".realTimeSendView")[open?"show":"hide"]();
	if(open && !window.webrtcCreate){
		var files=["assets/zdemo.index.realtime_voice.js","assets/zdemo.index.webrtc.js"];
		reclog(Html_$T("bzvb::正在加载{1} ...",0,JSON.stringify(files)));
		
		loadJsList(files);
	};
});

//ASR开关，实时语音识别、音频文件转文字
$(".asrSet").bind("change",function(e){
	var open=e.target.checked;
	$(".asrView")[open?"show":"hide"]();
	if(open && !window.asrInput){
		var file="assets/zdemo.index.asr.js";
		reclog(Html_$T("pmQM::正在加载{1} ...",0,file));
		
		loadJsList([file]);
	};
});
</script>










<script>
if(/mobile/i.test(navigator.userAgent)){
	//移动端加载控制台组件
	var elem=document.createElement("script");
	elem.setAttribute("type","text/javascript");
	elem.setAttribute("src","../assets/ztest-vconsole.js");
	document.body.appendChild(elem);
	elem.onload=function(){
		localStorage["vConsole_switch_y"]=localStorage["vConsole_switch_y"]||Math.min(innerHeight-100,500);
		new VConsole();
	};
};
</script>
<div style="padding:100px;"></div>









<script>
(function(){
	var i=0,arr=[],html=[];
	for(var k in TypeEngineImports){ i++;
		var o=TypeEngineImports[k]; o.sort=o.sort||i; o.type=k; arr.push(o);
	}
	arr.sort(function(a,b){return a.sort-b.sort});
	for(var i=0;i<arr.length;i++){
		var o=arr[i];
		html.push('<label><input type="radio" name="type" value="'+o.type+'">'+(o.name||o.type)+'</label>');
	}
	
	var prev;
	$(".types").html(html.join(" ")).bind("click",function(e){
		var input=$(e.target);
		if(input[0].nodeName=="LABEL"){
			input=$(input).find("input");
		};
		input=input[0]; if(!input || input.nodeName!="INPUT")return;
		var minjs=false//$(".loadMinJs")[0].checked;
		if(prev!=input||prev.minjs!==minjs){
			prev=input;
			prev.minjs=minjs;
			setTimeout(function(){
				loadEngine(input.value,1);
			});
		};
	});
})();

function loadEngine(type,isClick){
	var eImport=TypeEngineImports[type];
	if(!eImport)return;
	
	var srcs=[],mins=[],minsTxt=[],adds=[];
	for(var i=1,a=0;i<eImport.src.length;i++){
		var v=eImport.src[i];
		if(v===0){ a=1; continue; }
		if(a)adds.push(v);
		else srcs.push(v);
	};
	for(var i=0;i<eImport.dist.length;i++){
		var v=eImport.dist[i];
		if(v===0)break;
		if(/recorder-core\.js/.test(v)){
			v={url:v,check:function(){return !window.Recorder}};
		}
		mins.push(v); minsTxt.push(v.url||v);
	};
	
	var minjs=false//$(".loadMinJs")[0].checked;
	var engines=[].concat(minjs?mins:srcs, adds||[]);
	var end=function(){
		var enc=Recorder.prototype["enc_"+type];
		var tips=[];
		if(!enc){
			tips.push(Html_$T("QNa8::这个编码器无提示信息"));
		}else{
			if(enc.stable){
				tips.push(Html_$T("JcTD::{1}编码器稳定版，",0,type));
			}else{
				tips.push(Html_$T("V3bs::{1}编码器beta版，",0,type));
			};
			tips.push("<span style='color:");
			var fastMsg=enc.fast?Html_$T("WKT6::{1}转码超快",0,type):"";
			if(Recorder.prototype[type+"_start"]){
				tips.push("#0b1'>"+(fastMsg?fastMsg+" + ":"")+Html_$T("Dnkb::支持边录边转码(Worker)"));
			}else if(fastMsg){
				tips.push("#0b1'>"+fastMsg);
			}else{
				tips.push("red'>"+Html_$T("YQGp::仅支持标准UI线程转码"));
			};
			Html_$CallT(function(){
				tips.push("</span>, "+(enc.getTestMsg&&enc.getTestMsg()||enc.testmsg));
			});
		}
		var appJs=", src/app-support/app.js, src/app-support/app-*-support.js"+Html_$T("DPhZ::（环境相应的支持文件，部分还需要配置文件）");
		tips.push('<div style="color:green;padding-left:50px">');
		tips.push(Html_$T("mirC::使用{1}录音需要加载的js：",0,type));
		tips.push("<br>"+Html_$T("GHsx::【压缩版】：")+minsTxt.join(", ")+appJs.replace(/src\//g,"dist/"));
		tips.push("<br>"+Html_$T("XCXI::【源文件】：")+"src/recorder-core.js, "+srcs.join(", ")+appJs);
		tips.push("</div>");
		
		$(".typeTips").html(tips.join(""));
		
		resetPageTitle();
		if(isClick){
			keepTypeInHash(type,minjs);
		}
		reclog("<span style='color:#0b1'>"+typeTips()+Html_$T("lZEO::{1}已加载，可以录音了",0,"")+"</span>");
	};
	$(".typeTips").html(type+" engine loading...");
	var typeTips=function(){
		return minjs?Html_$T("FBTi::{1}编码器压缩版",0,type):Html_$T("c5BM::{1}编码器源码版",0,type);
	};
	if(!Recorder.prototype[type] || loadEngineState[type]!==minjs){
		reclog("<span style='color:#f60'>"+Html_$T("IzN2::正在加载{1}",0,"")+typeTips()+Html_$T("m196::，请勿操作...")+"</span>");
		loadJsList(engines,function(){
			loadEngineState[type]=minjs;
			
			end();
		},function(err){
			$(".typeTips").html('<span style="color:red">'+type+" engine loading error: "+err+'</span>');
			reclog(err,1);
		});
	}else{
		end();
	};
};
loadEngineState={};

var keepTypeInHash=function(type,minJs){
	var o={type:type};if(minJs)o.minJs=1;
	var hash=(location.hash||"#").replace(/(#?)&?recImport=([^&]*)/g,"$1");
	if(type!="mp3"){
		hash+=(hash=="#"?"":"&")+"recImport=";
		hash+=encodeURIComponent(JSON.stringify(o));
	}
	if(history.replaceState){
		history.replaceState(null, "", hash);
	}else{
		location.hash=hash;
	}
};
var resetPageTitle=function(){
	var type=$("[name=type]:checked").val();
	var title=document.title.replace(/^\[\w+\]/g,"");
	if(type!="mp3")title="["+type.toUpperCase()+"]"+title;
	document.title=title;
};

var loadJsList=function(jsList,True,False,allCheck){//False -> false继续加载，allCheck(urlItem)允许修改url值
	var rootUrl="../";
	var load=function(idx){
		if(idx>=jsList.length){
			True&&True();
			return;
		};
		var itm=jsList[idx];
		if(typeof(itm)=="string")itm={url:itm};
		if(itm.check && itm.check()===false){
			load(idx+1);
			return;
		};
		if(allCheck && allCheck(itm)===false){
			load(idx+1);
			return;
		};
		var url=itm.url;
		
		var elem=document.createElement("script");
		elem.setAttribute("type","text/javascript");
		elem.setAttribute("src",(/^\w+:/.test(url)?"":rootUrl)+url);
		if(!("onload" in elem)){//IsLoser 古董浏览器
			elem.onreadystatechange=function(){
				if(elem.readyState=="loaded"){
					elem.onload();
				}
			}
		};
		var isload=0;
		elem.onload=function(){
			if(!isload){
				load(idx+1);
				isload=1;
			}
		};
		elem.onerror=function(e){
			var v=False&&False(Html_$T("iXrp::js加载失败:")+(e.message||"-")+", "+url,1);
			if(v===false){
				elem.onload();
			}
		};
		$("head")[0].appendChild(elem);
	};
	setTimeout(function(){ load(0) });
};


(function(){try{
	//加载默认类型编码器
	var type=Import_RecJs_Set.type||"mp3";
	var els=$("input[name=type]"),el=0;
	for(var i=0;i<els.length;i++){
		if(els[i].value==type)el=els[i];
	}
	el.checked=true;
	if(Import_RecJs_Set.type){
		loadEngineState[type]=!!Import_RecJs_Set.minJs;
	}
	loadEngine(el.value);
}catch(e){console.error(e)}})();
</script>

<script>
reclog("<span style='color:#f60'>"+Html_$T("xEpa::正在执行Install，请勿操作...")+"</span>");
RecordApp.Install(function(){
	var isApp;
	RecordApp.Platforms.Native.Config.IsApp(function(v){isApp=v;});
	
	$(".recinfoCode").html($(".recinfoCodeTxt").val().replace(/\$\{(.+?)\}|\{\{([\S\s]+?)\}\}/g,function(a,b,c){return eval(b||c)}));
	
	reclog(Html_$T("J58f::Install成功，环境：")+RecordApp.Current.Key,2);
},function(err){
	reclog(Html_$T("zLUe::RecordApp.Install出错：")+err,1);
});

var callNative_showMemoryUsage=function(){
	AppJsBridgeRequest("debugInfo",{},function(data){
		if(data.status!="success"){
			reclog(Html_$T("uW1i::原生接口debugInfo调用出错：")+data.message,1);
		}else{
			var val=data.value.appMemoryUsage;
			if(val>0) val=(val/1024/1024).toFixed(2)+" MB";
			reclog(Html_$T("uW2i::占用内存大小(不一定准)：")+val,2);
		}
	});
};
var callNative_notifyService=function(show){
	var txt="一二三四五六七八九十";
	var args=show?{
		title:"录音中的标题七八九十"+txt+txt+txt+txt
		,content:"通知内容五六七八九十"+txt+txt+txt+txt+txt+txt+txt+txt+txt
	}:{
		close:true
	};
	
	if(show) reclog(Html_$T("zKd2::App中提升后台录音的稳定性：需要启用后台录音保活服务（iOS不需要），Android 9开始，锁屏或进入后台一段时间后App可能会被禁止访问麦克风导致录音静音、无法录音（App中H5录音也受影响），需要原生层提供搭配常驻通知的Android后台录音保活服务（Foreground services）"), "#4face6");
	AppJsBridgeRequest("androidNotifyService",args,function(data){
		if(data.status!="success"){
			reclog(Html_$T("RkeW::原生接口androidNotifyService调用出错：")+data.message,1);
		}else if(show){
			var nCode=data.value.notifyPermissionCode;
			var nMsg=data.value.notifyPermissionMsg;
			reclog(Html_$T("kDxI::搭配常驻通知的Android后台录音保活服务已打开，ForegroundService已运行(通知可能不显示或会延迟显示，并不影响服务运行)，通知显示状态(1有通知权限 3可能无权限)")+"code="+nCode+" msg="+nMsg,2);
		}else{
			reclog(Html_$T("KjdO::已关闭搭配常驻通知的Android后台录音保活服务"));
		}
	});
};
var callNative_setSpeakerOff=function(off){
	AppJsBridgeRequest("setSpeakerOff",{off:off,headset:off},function(data){
		var tag=Html_$T("Tw1M::[外放]");
		if(off)tag=Html_$T("Tw2M::[听筒播放]");
		if(data.status!="success"){
			reclog(tag+Html_$T("Tw3M::切换失败：")+data.message,1);
		}else{
			reclog(tag+Html_$T("Tw4M::已切换"),2);
		}
	});
};
</script>

<!-- 加载打赏挂件 -->
<script src="../assets/zdemo.widget.donate.js"></script>
<script>
DonateWidget({
	log:function(msg){reclog(msg)}
	,mobElem:$(".reclog").append('<div class="DonateView"></div>').find(".DonateView")[0]
});
</script>

<!-- 启用国际化多语言支持 -->
<script>
PageI18nWidget({
	elem:".i18nBox", rootUrl:"../"
	,titleKey:"PZM5"
	,langs:{
		"en-US":{urls:[ "#app_index_html/en-US.js","#widget_donate/en-US.js" ]}
	}
	,onUpdate:function(item){
		document.body.style.wordBreak=item.keyAs=="zh"?"break-all":"normal";
		resetPageTitle();
	}
});
</script>

</div>
</body>
</html>